package com.example.snailjob.service.impl;

import com.aizuda.snailjob.client.core.MethodResult;
import com.aizuda.snailjob.client.core.RetryCondition;
import com.aizuda.snailjob.client.core.annotation.Retryable;
import com.aizuda.snailjob.client.core.retryer.RetryType;
import com.example.snailjob.component.DefaultMethodResult;
import com.example.snailjob.exception.ParamException;
import com.example.snailjob.handler.LocalAndRemoteRetryHandler;
import com.example.snailjob.service.LocalRemoteService;
import com.example.snailjob.vo.OrderVo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

/**
 * 模拟先本地再远程重试案例
 *
 * @author www.byteblogs.com
 * @date 2023-07-18 22:19:30
 * @since 2.1.0
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class LocalRemoteServiceImpl implements LocalRemoteService {
    private final LocalAndRemoteRetryHandler localAndRemoteRetryHandler;

    @Override
    @Retryable(scene = "localRemote", retryStrategy = RetryType.LOCAL_REMOTE)
    public void localRemote() {
        System.out.println("local retry 方法开始执行");
        double i = 1 / 0;
    }

    /**
     * 使用先本地再远程的策略同步上传重试请求 retryStrategy = LOCAL_REMOTE 代表本地重试3次后再执行远程上报 async = false 代表使用同步上传的方式 timeout = 1 代表超时时间为1
     * unit = MINUTES 代表超时时间的单位是分钟
     */
    @Override
    @Retryable(scene = "remoteRetryWithSync", retryStrategy = RetryType.LOCAL_REMOTE,
            async = false, timeout = 1, unit = TimeUnit.MINUTES)
    public String remoteRetryWithLocalRemote(String requestId) {
        double i = 1 / 0;
        return requestId;
    }

    @Override
    @Retryable(scene = "localRetryWithPropagationRequired", retryStrategy = RetryType.LOCAL_REMOTE, retryIfResult = LocalRetryWithPropagationRequiredNeedRetry.class)
    public boolean localRetryWithPropagationRequired(String params, boolean isResult) {
        /**
         * 这里注意了，由于传播机制是Required, 这里会正常返回结果值，但是重试是被外部的Retryable控制，所以localRetryIfResultWithRequires不会触发重试
         * 这里如果需要外边触发重试，则需要对结果进行处理，外部重试需要感知到
         */
        OrderVo orderVo = localAndRemoteRetryHandler.localRetryIfResultWithRequires(params);
        if (orderVo.getOrderId().equals("RetryIfResult")) {
            // 这里抛异常也行 double i = 1 / 0;
            // 基于结果控制也行
            return false;
        }
        return false;
    }

    public static class LocalRetryWithPropagationRequiredNeedRetry implements RetryCondition {

        @Override
        public boolean shouldRetry(Object returnResult) {
            if (returnResult instanceof Boolean) {
                return !(Boolean) returnResult;
            }
            return false;
        }
    }

    @Override
    @Retryable(scene = "localRetryWithPropagationRequiredNew", retryStrategy = RetryType.LOCAL_REMOTE)
    public boolean localRetryWithPropagationRequiredNew(String params, boolean isResult) {
        if (isResult) {
            localAndRemoteRetryHandler.localRetryIfResultWithRequires(params);
        } else {
            localAndRemoteRetryHandler.localRetryWithRequiresNew(params);
        }

        return false;
    }

    @Override
    @Retryable(scene = "localRetryWithTryCatch1", retryStrategy = RetryType.LOCAL_REMOTE)
    public boolean localRetryWithTryCatch1(String params, boolean isResult) {
        try {
            // 内部方法需要触发重试
            if (isResult) {
                localAndRemoteRetryHandler.localRetryIfResultWithRequiresNew(params);
            } else {
                localAndRemoteRetryHandler.localRetryWithRequiresNew(params);
            }

        } catch (Exception e) {
            log.error("inner method encountered an exception", e);
        }
        return false;
    }

    @Override
    @Retryable(scene = "localRetryWithTryCatch2", retryStrategy = RetryType.LOCAL_REMOTE)
    public boolean localRetryWithTryCatch2(String params, boolean isResult) {
        // 由于传播机制为{REQUIRED}，异常被捕获,所以内部不会触发重试
        try {
            if (isResult) {
                localAndRemoteRetryHandler.localRetryIfResultWithRequires(params);
            } else {
                localAndRemoteRetryHandler.localRetryWithRequires(params);
            }

        } catch (Exception e) {
            log.error("inner method encountered an exception", e);
        }

        return false;
    }

    @Override
    public boolean localRetryWithTwoRetryMethod(String params) {
        localAndRemoteRetryHandler.retryMethod1(params);
        localAndRemoteRetryHandler.retryMethod2(params);
        return false;
    }

    @Retryable(scene = "localRetryWithIfResultLocalRemote", retryStrategy = RetryType.LOCAL_REMOTE, retryIfResult = NeedRetry.class,
            // 测试一下，预期的逻辑是不进入
            methodResult = TestRetryIfResultMethodResult.class)
    public OrderVo localRetryWithIfResultLocalRemote(String type) {
        if ("ParamException".equals(type)) {
            throw new ParamException("此处发生了指定异常Param Exception");
        } else {
            OrderVo orderVo = new OrderVo();
            orderVo.setOrderId(type);
            return orderVo;
        }
    }

    public static class NeedRetry implements RetryCondition {
        @Override
        public boolean shouldRetry(Object returnResult) {
            if (returnResult instanceof OrderVo orderVo) {
                return orderVo.getOrderId().equals("RetryIfResult");
            }

            return false;
        }
    }

    public static class TestRetryIfResultMethodResult implements MethodResult {

        @Override
        public Object result(String scene, String executorClassName, Object[] args, Throwable throwable) {
            throw new ParamException("这里有问题，不应该进入");
        }
    }

    @Retryable(scene = "localRetryWithMethodResult", retryStrategy = RetryType.LOCAL_REMOTE, isThrowException = false,
            methodResult = DefaultMethodResult.class)
    @Override
    public OrderVo localRetryWithMethodResult(String params) {
        throw new ParamException("此处发生了指定异常Param Exception");
    }
}
